public class Prolog {
    Unifier unifier;
    Stack<Choicepoint> choicepoints;
    Sink sink;
    
    public Prolog(Term query) {
        this(query,new Sink());
        
        sink = new MainSink(sink);
        sink = new TestSink(sink);
        
        sink = new DCGSink(sink);
        sink = new CompileSink(sink);
        sink = new ParseSink(sink);
        
        sink = new DynamicSink(sink);
        sink = new LibrarySink(sink);
        sink = new BootSink(sink);
        sink = new KitchenSink(sink);
    }
    
    public Prolog(Term query, Sink sink) {
        unifier = new Unifier();
        choicepoints = new Stack<Choicepoint>(new Choicepoint(null,0,query),null);
        this.sink = sink;
    }
    
    public Prolog spawn(Term query) {
        return new Prolog(query,sink);
    }
    
    public boolean run() {
        while(choicepoints != null) {
            Choicepoint choice = choicepoints.head;
            if(choice.dead) {
                choice.undoBindings();
                choicepoints = choicepoints.tail;
            }
            else if(choice.query == null) {
                choice.dead = true; // should this be here or not?
                return true;
            }
            else {
                Conjunct conjunct = choice.query.head;
                Compound functor = (Compound)conjunct.term.dereference();
                Stack<Choicepoint> choicepoints2;
                int choiceIndex = conjunct.choice;
                
                //System.out.println("  %"+functor.name+"/"+functor.arity);
                //System.out.println("  %"+functor);
                
                choicepoints.head = choicepoints.head.incrementQueryHeadChoice();
                choicepoints2 = sink.fishFor
                    (functor.name, functor.arity, functor, choiceIndex,
                     this, unifier, conjunct.cutIndex,
                     choice.query.tail, choicepoints);
                
                if(choicepoints2 == null) {
                    choice.dead = true;
                }
                else {
                    choicepoints = choicepoints2;
                }
            }
        }
        
        return false;
    }
    
    public static void main(String args[]) {
        Term query = new Compound("main");
        Prolog engine = new Prolog(query);
        while(engine.run()) System.out.println(query + ".");
    }
}
